package com.simpou.commons.utils.functional;

import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;

import java.util.HashMap;
import java.util.Map;

/**
 * Operações úteis sobre ações.
 *
 * @author Jonas Pereira
 * @version 2013-06-26
 * @since 2013-06-26
 */
public class Actions {

    public static <T, E> E execute(Action<T, E> action, T object) {
        E result;
        try {
            result = action.execute(object);
        } catch (Exception ex) {
            action.doOnError(object, ex);
            result = null;
        }
        return result;
    }

    private final Map<CachedExecution, Object> executionCache = new HashMap<CachedExecution, Object>();

    @EqualsAndHashCode
    @RequiredArgsConstructor
    private class CachedExecution {

        private final String operation;

        private final Object operator;

    }

    /**
     * Executa uma operação de forma condicional, caso a mesma ainda não tenha sido executada considerando o par
     * operação e operador. A operação é identificada pelo equals do nome de sua classe e o operador pelo equals do
     * próprio objeto.
     *
     * @param operation Operação a ser executada se a mesma ainda não foi executada para o operador em questão.
     * @param operator  Operador a ser utilizado operação.
     * @return Resultado da execução da operação sobre o operador ou valor armazenado resultante de uma chamada
     * anterior.
     */
    public <T, E> E cachedExecute(final Action<T, E> operation, final T operator) {
        final CachedExecution execution = new CachedExecution(operation.getClass().getName(), operator);
        if (executionCache.containsKey(execution)) {
            return (E) executionCache.get(execution);
        } else {
            final E result = execute(operation, operator);
            executionCache.put(execution, result);
            return result;
        }
    }
}
